#include <util/delay.h>
#include "enc28j60.h"
#include <avr/io.h>
#include "smartprintf.h"
//#include <stdio.h>
//#include <util/delay.h>



#define MIN(a,b)			((a<b)?(a):(b))
#define MAX(a,b)			((a>b)?(a):(b))
#define ABS(x)				((x>0)?(x):(-x))

const uint8_t mymac_addr[] = { ENC_MAC0, ENC_MAC1, ENC_MAC2, ENC_MAC3, ENC_MAC4, ENC_MAC5 };

uint8_t enc_bank;
uint16_t next_packetptr;
#define ENC_CONTROL_PORT	PORTB
#define ENC_CONTROL_DDR	DDRB
#define ENC_CONTROL_CS		2



void enc_init(void)
{
	// initialize I/O

	ENC_CONTROL_DDR |= 1<<ENC_CONTROL_CS;
	ENC_CONTROL_PORT |= 1<<ENC_CONTROL_CS;
	

	
	PORTB |= 1<<PB5; //SCK HI
	DDRB  |= 1<<PB2 |1<<PB3 | 1<<PB5; // mosi, sck, ss output
	DDRB  &= ~(1<<PB4); // miso input
	
	// initialize SPI interface
	SPCR |= 1<<MSTR | 1<<SPE;	//Mastermode and enable SPI mode 0,0
	SPSR |= 1<<SPI2X;
	

	/* hardware reset 
	// enc reset 
	enc_writeop(ENC_SOFT_RESET, 0, ENC_SOFT_RESET);
	// wait 50us
	_delay_us(50);
	// dont do anything before the clkrdy bit is set
	while(!(enc_read_reg(ESTAT) & ESTAT_CLKRDY));
 	*/

	_delay_ms(1000);


	/* Bank0 Buffer init */
	//set Receivebuffer start address
	next_packetptr = RXSTART_INIT;
	enc_writereg(ERXSTL, RXSTART_INIT&0xFF);
	enc_writereg(ERXSTH, RXSTART_INIT>>8);

	// set receive pointer address
	enc_writereg(ERXRDPTL, RXSTART_INIT&0xFF);
	enc_writereg(ERXRDPTH, RXSTART_INIT>>8);

	// set receive buffer end
	// ERXND defaults to 0x1FFF (end of ram)
	enc_writereg(ERXNDL, RXSTOP_INIT&0xFF);
	enc_writereg(ERXNDH, RXSTOP_INIT>>8);

	// set transmit buffer start
	// ETXST defaults to 0x0000 (beginnging of ram)
	enc_writereg(ETXSTL, TXSTART_INIT&0xFF);
	enc_writereg(ETXSTH, TXSTART_INIT>>8);
/*
	#ifdef DEBUG
	smartprintf("buffer\n\r");
	smartprintf("\n\rRX start: %#3x%2x\n\r", enc_readreg(ERXSTH), enc_readreg(ERXSTL));
	smartprintf("RX Ptr: %#x%x\n\r", enc_readreg(ERXRDPTH), enc_readreg(ERXRDPTL));
	smartprintf("RX end: %#x%x\n\r", enc_readreg(ERXNDH), enc_readreg(ERXNDL));
	smartprintf("TX start: %#x%x\n\r", enc_readreg(ETXSTH), enc_readreg(ETXSTL));
	#endif
*/
	/* Bank2 MAC init */
	//enable sending and receiving pause controll frames
	// enable MAC receive
	enc_writereg(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
	//clear all MAC reset bits
	enc_writereg(MACON2, 0x00);
	// enable automatic padding and crc 
	enc_writeop(ENC_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);

	// set non back-to-back interframe gap
	enc_writereg(MAIPGL, 0x12);
	enc_writereg(MAIPGH, 0x0C);
	//set back-to-back interframe gap
	enc_writereg(MABBIPG, 0x12);
	//set maximum framelength
	enc_writereg(MAMXFLL, MAX_FRAMELEN&0xFF);	
	enc_writereg(MAMXFLH, MAX_FRAMELEN>>8);

/*
	#ifdef DEBUG
	smartprintf("\n\rMACON1: %#x\n\r", enc_readreg(MACON1));
	smartprintf("MACON2: %#x\n\r", enc_readreg(MACON2));
	smartprintf("MACON3: %#x\n\r", enc_readreg(MACON3));
	smartprintf("nbtb gap: %#x%x\n\r", enc_readreg(MAIPGL), enc_readreg(MAIPGH));
	smartprintf("btb gap: %#x\n\r", enc_readreg(MABBIPG));
	smartprintf("max Flength: %#x%x\n\r", enc_readreg(MAMXFLL), enc_readreg(MAMXFLH));
	#endif
*/
	// do bank 3 stuff
	// write MAC address
	// NOTE: MAC address in ENC28J60 is byte-backward
	uint8_t i;
	uint8_t maadr = MAADR1;
	for(i = 0; i < 6; i++)
	{
		enc_writereg(maadr++, mymac_addr[i]);
	}
/*
	#ifdef DEBUG
	smartprintf("bank %d\n\r", enc_bank);
	smartprintf("\n\rMAADR0 register: %#x\n\r", enc_readreg(MAADR0));
	smartprintf("MAADR1 register: %#x\n\r", enc_readreg(MAADR1));
	smartprintf("MAADR2 register: %#x\n\r", enc_readreg(MAADR2));
	smartprintf("MAADR3 register: %#x\n\r", enc_readreg(MAADR3));
	smartprintf("MAADR4 register: %#x\n\r", enc_readreg(MAADR4));
	smartprintf("MAADR5 register: %#x\n\r", enc_readreg(MAADR5));
	#endif 
*/
	//disable halfduplex loopback
	enc_writephy(PHCON2, PHCON2_HDLDIS);

	//bank0
	enc_setbank(ECON1);
	//enable interrupts: global Ints (enables INTpin), receive pending interrupt
	enc_writeop(ENC_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
	// enable packet reception
	enc_writeop(ENC_BIT_FIELD_SET, ECON1, ECON1_RXEN);

}


uint8_t enc_readop(uint8_t op, uint8_t address)
{
	uint8_t data;
   
	// enc28j60 SPI Slave Select -> CS = LOW
	ENC_CONTROL_PORT &= ~(1<<ENC_CONTROL_CS);
	
	// writing one byte into SPDR triggers transfer
	// after complete transmission SPIF bit is set and SPDR holds received result byte
	// send command = 3bit opcode & 5bit adress
	SPDR = op | (address & ADDR_MASK);
	while(!(SPSR & (1<<SPIF)));
	//send dummy byte for clock generation that the enc needs to shift out result
	SPDR = 0x00;
	while(!(SPSR & (1<<SPIF)));
	//if PHY or MAC Register is read, first a dummy byte is send 
	if(address & 0x80)
	{
		SPDR = 0x00;
		while(!(SPSR & (1<<SPIF)));
	}
	data = SPDR;
	
	//enc28j60 SPI relaese -> CS = HIGH
	ENC_CONTROL_PORT |= (1<<ENC_CONTROL_CS);

	return data;
}

/* SPI Instruction - Write Operation */
void enc_writeop(uint8_t op, uint8_t address, uint8_t data)
{

	ENC_CONTROL_PORT &= ~(1<<ENC_CONTROL_CS);

	SPDR = op | (address & ADDR_MASK);
	while(!(SPSR & (1<<SPIF)));
	SPDR = data;
	while(!(SPSR & (1<<SPIF)));


	ENC_CONTROL_PORT |= (1<<ENC_CONTROL_CS);
}

void enc_readbuffer(uint16_t len, uint8_t* data)
{
	ENC_CONTROL_PORT &= ~(1<<ENC_CONTROL_CS);
	
	SPDR = ENC_READ_BUF_MEM;
	while(!(SPSR & (1<<SPIF)));
	while(len--)
	{
		SPDR = 0x00;
		while(!(SPSR & (1<<SPIF)));
		*data++ = SPDR;
	}	

	ENC_CONTROL_PORT |= (1<<ENC_CONTROL_CS);
}

void enc_writebuffer(uint16_t len, uint8_t* data)
{
	ENC_CONTROL_PORT &= ~(1<<ENC_CONTROL_CS);
	
	SPDR = ENC_WRITE_BUF_MEM;
	while(!(SPSR & (1<<SPIF)));
	while(len--)
	{
		SPDR = *data++;
		while(!(SPSR & (1<<SPIF)));
	}	
	ENC_CONTROL_PORT |= (1<<ENC_CONTROL_CS);
}

void enc_setbank(uint8_t address)
{
	// set the bank (if needed)
	if((address & BANK_MASK) != enc_bank)
	{
		// set the bank
		enc_writeop(ENC_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
		enc_writeop(ENC_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
		enc_bank = (address & BANK_MASK);
	}
}

/* Read Register except Phy Registers */
uint8_t enc_readreg(uint8_t address)
{
	enc_setbank(address);
	return enc_readop(ENC_READ_CTRL_REG, address);
}

/* Write Register except Phy Registers */
void enc_writereg(uint8_t address, uint8_t data)
{
	enc_setbank(address);
	enc_writeop(ENC_WRITE_CTRL_REG, address, data);
}



void enc_writephy(uint8_t address, uint16_t data)
{
	//To write to a PHY register:
	//1.	Write the address of the PHY register to write to
    //	  	into the MIREGADR register.
	//2.  	Write the lower 8 bits of data to write into the
    //	  	MIWRL register.
	//3.  	Write the upper 8 bits of data to write into the
    //	  	MIWRH register. Writing to this register auto-
    //	  	matically begins the MII transaction, so it must
    //		be written to after MIWRL. The MISTAT.BUSY
    //		bit becomes set.

	enc_writereg(MIREGADR, address);
	enc_writereg(MIWRL, data);	
	enc_writereg(MIWRH, data>>8);

	while(enc_readreg(MISTAT) & MISTAT_BUSY);
}



void enc_sendpacket(uint16_t len, uint8_t* packet)
{
	//set write Pointer start of TX Buffer area
	enc_writereg(EWRPTL, TXSTART_INIT);
	enc_writereg(EWRPTH, TXSTART_INIT>>8);
	//set TX end Pointer to the end of the packet
	enc_writereg(ETXNDL, (TXSTART_INIT+len));
	enc_writereg(ETXNDH, (TXSTART_INIT+len)>>8);

	// write per-packet control byte
	enc_writeop(ENC_WRITE_BUF_MEM, 0, 0x00);

	// copy the packet into the transmit buffer
	enc_writebuffer(len, packet);
	
	// send the contents of the transmit buffer onto the network
	enc_writeop(ENC_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}

uint16_t enc_readpacket(uint16_t maxlen, uint8_t* packet)
{
	uint16_t rxstat;
	uint16_t len;

	// check if a packet has been received and buffered
	if( !(enc_readreg(EIR) & EIR_PKTIF) )
		return 0;
	
	// Make absolutely certain that any previous packet was discarded	
	//if( WasDiscarded == FALSE)
	//	MACDiscardRx();

	// Set the read pointer to the start of the received packet
	enc_writereg(ERDPTL, (next_packetptr));
	enc_writereg(ERDPTH, (next_packetptr)>>8);

	// read the next packet pointer
	next_packetptr  = enc_readop(ENC_READ_BUF_MEM, 0);
	next_packetptr |= enc_readop(ENC_READ_BUF_MEM, 0)<<8;

	// read the packet length
	len  = enc_readop(ENC_READ_BUF_MEM, 0);
	len |= enc_readop(ENC_READ_BUF_MEM, 0)<<8;

	// read the receive status
	rxstat  = enc_readop(ENC_READ_BUF_MEM, 0);
	rxstat |= enc_readop(ENC_READ_BUF_MEM, 0)<<8;

	// limit retrieve length
	// (we reduce the MAC-reported length by 4 to remove the CRC)
	len = MIN(len, maxlen);

	// copy the packet from the receive buffer
	enc_readbuffer(len, packet);

	// Move the RX read pointer to the start of the next received packet
	// This frees the memory we just read out
	enc_writereg(ERXRDPTL, (next_packetptr));
	enc_writereg(ERXRDPTH, (next_packetptr)>>8);

	// decrement the packet counter indicate we are done with this packet
	enc_writeop(ENC_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);

	return len;
}



void enc_pktcnt(void)
{
//	smartprintf("PKTIF: %d\n\r", (enc_readreg(EIE) & EIR_PKTIF));
//	smartprintf("PacketCounter: %d\n\r", enc_readreg(EPKTCNT));
//	enc_writeop(ENC_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}



